package com.yulian.web_api.supplier_management;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import com.healthmarketscience.sqlbuilder.CustomSql;
import com.healthmarketscience.sqlbuilder.InCondition;
import com.healthmarketscience.sqlbuilder.SelectQuery;
import com.yulian.repository.CommonRepositoryImpl;
import com.yulian.repository.system_management.SystemMenuRepositoryImpl;
import com.yulian.util.CommonUtils;
import com.yulian.util.DataUtils;

import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.Log4JLoggerFactory;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.pgclient.PgPool; 
import io.vertx.sqlclient.Tuple;

/**
 * 该类位于业务逻辑层，主要实现系统菜单数据查询，新增数据，更新数据，删除数据的业务逻辑，为MainVerticle中的REST接口提供服务
 * @author 余大勇
 *
 */
public class SystemMenuManager {
	final InternalLogger logger = Log4JLoggerFactory.getInstance(SystemMenuManager.class);
	/***
	 * 其余访问数据库获取数据的类
	 */ 
	CommonRepositoryImpl commonRepository = new CommonRepositoryImpl();
	SystemMenuRepositoryImpl systemMenuRepository = new SystemMenuRepositoryImpl(); 
	
	/**
	 * 构建查询满足条件的sql语句
	 * @param queryJS
	 * @param query_offset
	 * @param query_limit
	 * @return
	 */
	public String createSelectSql(JsonObject queryJS, int query_offset, int query_limit) {
		// 定义 SelectQuery 对象  
		SelectQuery sq = new SelectQuery(); 
		sq.addCustomColumns(new CustomSql("id"), new CustomSql("menu_code"), new CustomSql("menu_name"), new CustomSql("parent_code"), new CustomSql("parent_name"),
				 new CustomSql("menu_path"), new CustomSql("menu_icon"), new CustomSql("is_leaf"), new CustomSql("is_button"),new CustomSql("is_delete"),new CustomSql("sort_order"), new CustomSql("create_user"), 
				 new CustomSql("update_user"), new CustomSql("create_time"), new CustomSql("update_time") )
		.addCustomFromTable("system_menu"); 
		//通过循环取出Json对象中的查询参数，并添加到 InCondition中 
		Iterator<String> iter =   queryJS.fieldNames().iterator();  
		while (iter.hasNext()) { 
			String key = iter.next(); 
			try { 
			  String value = queryJS.getString(key); 
			  if( !value.equals("") && !value.equals("[]") ) {
				  String firstChar = value.substring(0,1);
				  if(firstChar.equals("["))
					  value = value.substring(1,value.length()-1);
				  value = value.replace(", ", "','");   
				  //添加含InCondtion的条件的where子句
				  sq.getWhereClause().addCustomCondition(new InCondition( new CustomSql(key),  value));
			  }  
			} catch (Exception e) { 
				logger.info("creaStrGetRecordsWithJson, 发生错误,错误原因: ",e.getMessage()); 
			}
		}   
	   	return sq.validate().toString() + " order by  id desc  limit " + query_limit + " offset " + query_offset ;  
    }
	
	/**
	 * 根据传入的查询参数获取满足条件的记录
	 * @param client
	 * @param config
	 * @param bodyJSObj
	 * @return
	 */
	 public Future<JsonObject> getRecords(PgPool client, JsonObject config, JsonObject bodyJSObj) {
		 Promise<JsonObject> promise = Promise.promise();
		 //最后返回的JsonObject对象
		 JsonObject responseJSObj = new JsonObject();
        // 验证必须传递字符串字段 
        List<String> must_fields = Arrays.asList("menu_name_list", "is_delete_list");
        JsonObject must = this.commonRepository.verifyAllParamMust(bodyJSObj, must_fields);
        if (must != null) {
            promise.complete(must);
            return promise.future();
        } 
        // 验证必须传的数值字段
 		List<String>  numeric_fields = Arrays.asList( "page_size", "page_index");
 		JsonObject numeric = this.commonRepository.verifyAllParamNumeric(bodyJSObj, numeric_fields);
 		if (numeric != null) {
 			promise.complete(numeric);  
 			return promise.future();
 		}
 		//从bodyJson中获取数据
		String menu_name_list = bodyJSObj.getString("menu_name_list"); 
		String is_delete_list = bodyJSObj.getString("is_delete_list");  
		int page_size = Integer.valueOf(bodyJSObj.getString("page_size"));
		int page_index = Integer.valueOf(bodyJSObj.getString("page_index")); 
		int query_offset = (page_index - 1) * page_size;
	    int query_limit = page_size;
	    logger.info("menu_name_list {}",menu_name_list);
	    logger.info("is_delete_list {}",is_delete_list);
		//用于传递给构建sql的JsonObject
		JsonObject queryJSObj = new JsonObject();
		queryJSObj.put("menu_name", menu_name_list); 
	    queryJSObj.put("is_delete", is_delete_list); 
          
        this.commonRepository.getCon(client).onSuccess(conn -> {
        	//获取满足条件的记录总数,传入 数据表名
            String count_sql = this.commonRepository.createCountSql(queryJSObj, "system_menu");
            Future<Integer> getTotalFuture = this.commonRepository.getRecordsCount(conn, count_sql, "get_system_menu");
            //获取满足条件的记录
            String sql = createSelectSql(queryJSObj, query_offset, query_limit);
            Future<JsonArray> getRecordsFuture = this.commonRepository.queryJsonArray(conn, sql, "getSystemMenuRecords");  
            CompositeFuture.join(Arrays.asList(getTotalFuture, getRecordsFuture)).onComplete(ar2 -> {
                if (ar2.succeeded()) { 
                    int total_records = Integer.valueOf(ar2.result().list().get(0).toString());
                    JsonArray resultJSArray = (JsonArray) ar2.result().list().get(1);   
                    //从配置文件中读取  
                    JsonObject is_delete_info = config.getJsonObject("DELETE_INFO");
        			JsonObject is_leaf_info = config.getJsonObject("LEAF_INFO");
        			JsonObject is_button_info = config.getJsonObject("BUTTON_INFO");
                    // 最后组装含有name的Json对象和数组
                    JsonArray recordsJSArray = new JsonArray();  
                    for (int i = 0; i < resultJSArray.size(); i++) {
                    	JsonObject record_obj = resultJSArray.getJsonObject(i); 
                    	record_obj.put("is_leaf_name",is_leaf_info.getString(record_obj.getValue("is_leaf").toString()) ); 
                    	record_obj.put("is_button_name",is_button_info.getString(record_obj.getValue("is_button").toString()) );  
                        record_obj.put("is_delete_name", is_delete_info.getString(record_obj.getString("is_delete")));   
                        recordsJSArray.add(record_obj);
                    } 
                    JsonObject dataJSObj = new JsonObject();
                    dataJSObj.put("total", total_records).put("records", recordsJSArray);
                    responseJSObj.put("code", 200).put("msg", "获取记录成功").put("data", dataJSObj); 
                    promise.complete(responseJSObj);
                } else {
                    logger.info("获取记录数据发生错误: {}", ar2.cause().getMessage());
                    promise.complete(CommonUtils.createExceptionJsonObject(ar2, "getSystemMenuRecords", "获取系统菜单失败"));
                }
            });
        }).onFailure(err -> promise.fail(CommonUtils.createJsonObject(err, "getSystemMenuRecords", "数据库连接失败")));
        return promise.future();
    }  
	 
	 /**
     * 该方法实现接收web前端通过body传入的参数，调用通用类CommonRepositoryImpl的方法实现数据的增、删、改 。
     * 通过含参数的Json对象bodyJSObj，通过method字段获取操作类型，分别是insert，update, delete, admit, block。
     * 1) 操作类型为 insert，首先查询传入的系统菜单名是否存在，如果不存在就新增数据，存在就更新数据。
     * 2) 操作类型为 update，根据id更新记录  
     * @param client    连接池对象
     * @param config    配置对象
     * @param bodyJSObj body参数对象
     * @param user      操作用户
     * @return 操作的结果
     * @throws Exception 
     */
    public Future<JsonObject> insertOrUpdate(PgPool client, JsonObject config, JsonObject bodyJSObj, User user)  {
        Promise<JsonObject> promise = Promise.promise();
        int user_id = user.attributes().getJsonObject("accessToken").getInteger("userId");
        String create_user = user.attributes().getJsonObject("accessToken").getString("username"); 
        /**
         *  数据表 system_menu 字段注释 
			menu_code	菜单编码
			menu_name	菜单标题
			parent_code	父级编码
			menu_path	菜单路由
			menu_icon	菜单图标 
			is_leaf	是否叶子菜单
			is_button	是否按钮
			is_delete	是否有效 
			sort_order	排序序号 
         */
        // 验证必须传递的字段
		List<String>  must_fields = Arrays.asList("method", "menu_name", "parent_code", "parent_name" );
		JsonObject must = this.commonRepository.verifyAllParamMust(bodyJSObj, must_fields);
		if (must != null) {
			promise.complete(must);  
			return promise.future();
		} 
		// 验证数值字段
		List<String>  numeric_fields = Arrays.asList("is_leaf", "is_button","sort_order", "id");
		JsonObject numeric = this.commonRepository.verifyAllParamNumeric(bodyJSObj, numeric_fields);
		if (numeric != null) {
			promise.complete(numeric);  
			return promise.future();
		} 
        String method = bodyJSObj.getString("method");
        String menu_name = bodyJSObj.getString("menu_name");
		String parent_code = bodyJSObj.getString("parent_code");
		String parent_name = bodyJSObj.getString("parent_name"); 
		String menu_path = bodyJSObj.getString("menu_path");
		String menu_icon = bodyJSObj.getString("menu_icon");
		int is_leaf = Integer.valueOf(bodyJSObj.getString("is_leaf")); 
		int is_button = Integer.valueOf(bodyJSObj.getString("is_button"));
		int sort_order = Integer.valueOf(bodyJSObj.getString("sort_order"));
        long id = Long.valueOf(bodyJSObj.getString("id"));  
        this.commonRepository.getCon(client).onSuccess(conn -> {  
              
            if (method.equals("insert")) {  
            	// 定义获取system_menu_name数量的future 
            	String menu_name_count_sql =  "select count(*) as total from system_menu where menu_name = $1 and parent_code=$2";
            	Future<Integer> getMenuNameCountFuture = this.commonRepository.getRecordsCount(conn, menu_name_count_sql, Tuple.of(menu_name, parent_code), "get_system_menu_name", "查询系统菜单名字 操作失败");
                // 定义获取system_menu_code的future
            	String system_menu_code_sql = "select menu_code from system_menu  order by id desc limit 1";
                Future<String> getSytemMenuCodeFuture = this.commonRepository.getCodeFuture(conn, system_menu_code_sql,  "get_system_menu_code", "查询系统菜单编码 操作失败");
                // 并行执行多个future
                CompositeFuture.join(Arrays.asList(getMenuNameCountFuture, getSytemMenuCodeFuture )).onComplete(ar -> {
                    if (ar.succeeded()) {
                        // 获取执行查询名字和编码的结果
                        int exist_number = Integer.valueOf(ar.result().list().get(0).toString());
                        String exist_menu_code = ar.result().list().get(1).toString(); 
                        int[] menu_codes = new int[1];
                        int new_menu_code = 100000;
                        // 为new_system_menu_code赋值
                        if (exist_number == 0 && !exist_menu_code.equals("")) {
                        	new_menu_code = Integer.valueOf(exist_menu_code) + 1;
                        } else if (exist_number == 0 && exist_menu_code.equals("")) {
                        	new_menu_code = 100000;
                        }
                        menu_codes[0] = new_menu_code; 
                        // 如果不存在该system_menu_name,就新增
                        if (exist_number == 0) {
                        	/*** 
    		  				 * 方法1: 新增数据 
    		  				 */ 
	  				    	this.systemMenuRepository.insertSystemMenu(conn, String.valueOf(menu_codes[0]), menu_name, parent_code, parent_name, menu_path, menu_icon, is_leaf, is_button, sort_order, create_user)                                                             
      				  		.onSuccess(resultJSObj -> {    
      				  			//写入操作日志 
			  					this.commonRepository.writePlatformLog(conn, user_id, "/web/system_menu/insert", bodyJSObj, resultJSObj, "")
			  					.onComplete( ar3 -> {  
			  	            		promise.complete(resultJSObj);
			  					}); 
                            }).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "insertSytemMenu", "新增系统菜单失败"))); 
                        } else if (exist_number > 0) {
                        	promise.fail(CommonUtils.createExceptionJsonObjectStr(-1, "insertSytemMenu", "新增系统菜单发生错误,该名称已经存在"));  
                        }
                    } else { 
                        promise.fail(CommonUtils.createExceptionJsonObjectStr(ar, "insertSytemMenu", "新增数据发生错误")); 
                    }
                });
            } else if (method.equals("update")) { 
            	String menu_code = bodyJSObj.getString("menu_code");
            	String menu_name_count_sql = "select count(*) as total from system_menu where menu_name = $1 and parent_code = $2 and id!=$3"; 
            	this.commonRepository.getRecordsCount(conn, menu_name_count_sql, Tuple.of(menu_name,parent_code, id), "get_system_menu_name", "查询系统菜单名字 操作失败")
            	.onSuccess(total -> { 
            		if(total == 0) { 
    					this.systemMenuRepository.updateSystemMenu(conn, id, menu_code, menu_name,  parent_code, parent_name, menu_path, menu_icon, is_leaf, is_button, sort_order, create_user)                                                                 
    	          		.onSuccess(resultJSObj -> {    
    	          			//写入操作日志 
		  					this.commonRepository.writePlatformLog(conn, user_id, "/web/system_menu/update", bodyJSObj, resultJSObj, "")
		  					.onComplete( ar3 -> { 
		  	            		promise.complete(resultJSObj);
		  					}); 
    	          		}).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "updateSytemMenu", "更新系统菜单数据失败"))); 	
    	     			 
          			}else {
          				promise.fail(CommonUtils.createExceptionJsonObjectStr(-1, "updateSytemMenu", "修改后系统菜单名已经存在")); 
          			} 
            	}).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "get_system_menu_name", "获取系统菜单记录失败")));  
            } 
        }).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "getSytemMenuRecords", "数据库连接失败"))); 
        return promise.future();
    } 
    
    /**
     * 该方法实现接收web前端通过body传入的参数，调用数据访问层CommonRepositoryImpl类的方法实现数据的逻辑删除或物理删除
     * 通过含参数的Json对象bodyJSObj，通过method字段获取操作类型，分别是  delete  
     * @param client    连接池对象
     * @param config    配置对象
     * @param bodyJSObj body参数对象
     * @param user      操作用户
     * @return 操作的结果
     * @throws Exception 
     */
    public Future<JsonObject> delete(PgPool client, JsonObject bodyJSObj, User user)  {
        Promise<JsonObject> promise = Promise.promise();
        int user_id = user.attributes().getJsonObject("accessToken").getInteger("userId");
        String create_user = user.attributes().getJsonObject("accessToken").getString("username"); 
        // 验证必须传递的字段
 		List<String>  must_fields = Arrays.asList("method",  "id_list");
 		JsonObject must = this.commonRepository.verifyAllParamMust(bodyJSObj, must_fields);
 		if (must != null) {
 			promise.complete(must);  
 			return promise.future();
 		}
        // 获取操作类型字段的值（必须字段）
        String method = bodyJSObj.getString("method");
        // 获取选中记录的id
        String id_list = bodyJSObj.getString("id_list"); 
         
        // 获取bodyJSObj参数对象中字段的值, 各字段注释请参看数据库设计文档中数据表supplier_info的说明
        this.commonRepository.getCon(client).onSuccess(conn -> {
        	if (method.equals("physical_delete")) { 
    			this.systemMenuRepository.physicalDeleteSystemMenu(conn, id_list, create_user)                                                                 
	      		.onSuccess(resultJSObj -> {    
	      			//写入操作日志 
  					this.commonRepository.writePlatformLog(conn, user_id, "/web/system_menu/physical_delete", bodyJSObj, resultJSObj, "")
  					.onComplete( ar3 -> { 
  	            		promise.complete(resultJSObj);
  					}); 
	      		}).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "updateSytemMenu", "更新系统菜单数据失败")));  
            }  
        }).onFailure(err -> promise.fail(CommonUtils.createExceptionJsonObjectStr(err, "admit_supplier", "数据库连接失败"))); 
        return promise.future();
    } 
     

}
